home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / webbrowser / webbrowser.c < prev   
Encoding:
C/C++ Source or Header  |  2003-05-16  |  25.1 KB  |  961 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  * Copyright (C) 1997 Misha Dynin
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  */
  19.  
  20. /*
  21.     Web Browser v0.3 -- opens a URL in Netscape
  22.  
  23.         by Misha Dynin <misha@xcf.berkeley.edu>
  24.  
  25.     For more information, see webbrowser.readme, as well as
  26.  
  27.         http://www.xcf.berkeley.edu/~misha/gimp/
  28.  
  29.  */
  30.  
  31. #include "config.h"
  32.  
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #ifdef HAVE_UNISTD_H
  37. #include <unistd.h>
  38. #endif
  39. #include <errno.h>
  40. #include <ctype.h>
  41. #ifdef __EMX__
  42. #include <process.h>
  43. #endif
  44.  
  45. #include <gtk/gtk.h>
  46.  
  47. #ifdef G_OS_WIN32
  48. #define STRICT
  49. #include <windows.h>
  50. #include <shellapi.h>
  51. #else
  52. #include <X11/X.h>
  53. #include <X11/Xatom.h>
  54. #include <X11/Xlib.h>
  55. #include <X11/Xmu/WinUtil.h>    /* for XmuClientWindow() */
  56. #endif
  57.  
  58. #include <libgimp/gimp.h>
  59. #include <libgimp/gimpui.h>
  60.  
  61. #include "libgimp/stdplugins-intl.h"
  62.  
  63.  
  64. /* Browser program name -- start in case it's not already running */
  65. #define BROWSER_PROGNAME    "netscape"
  66.  
  67. /* Open a new window with request? */
  68. #define OPEN_URL_NEW_WINDOW    1
  69. #define OPEN_URL_CURRENT_WINDOW    0
  70.  
  71. #define URL_BUF_SIZE 255
  72.  
  73.  
  74. static void query (void);
  75. static void run   (gchar   *name,
  76.            gint     nparams,
  77.            GimpParam  *param,
  78.            gint    *nreturn_vals,
  79.            GimpParam **return_vals);
  80.  
  81. static gint open_url_dialog     (void);
  82. static void ok_callback         (GtkWidget *widget,
  83.                  gpointer   data);
  84. static void about_callback      (GtkWidget *widget,
  85.                  gpointer   data);
  86. static void new_window_callback (GtkWidget *widget,
  87.                  gpointer   data);
  88. static void url_callback        (GtkWidget *widget,
  89.                  gpointer   data);
  90. #ifndef G_OS_WIN32
  91. static gint mozilla_remote      (gchar     *command);
  92. #endif
  93.  
  94. static gint open_url            (gchar     *url,
  95.                  gint       new_window);
  96.  
  97.  
  98. GimpPlugInInfo PLUG_IN_INFO =
  99. {
  100.   NULL,  /* init_proc  */
  101.   NULL,  /* quit_proc  */
  102.   query, /* query_proc */
  103.   run,   /* run_proc   */
  104. };
  105.  
  106. typedef struct
  107. {
  108.   gchar url[URL_BUF_SIZE];
  109.   gint  new_window;
  110. } u_info;
  111.  
  112. static u_info url_info =
  113. {
  114.   "http://www.gimp.org/",  /* Default URL */
  115.   OPEN_URL_NEW_WINDOW,     /* Change to ...CURRENT_WINDOW if
  116.                 * you prefer that as the default
  117.                 */
  118. };
  119.  
  120. static gboolean run_flag = FALSE;
  121.  
  122. MAIN ()
  123.  
  124. static void
  125. query (void)
  126. {
  127.   static GimpParamDef args[] =
  128.   {
  129.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  130.     { GIMP_PDB_STRING, "url", "URL of a document to open" },
  131.     { GIMP_PDB_INT32,  "new_window", "Create a new window or use existing one?" },
  132.   };
  133.   static gint nargs = sizeof (args) / sizeof (args[0]);
  134.  
  135.   gimp_install_procedure ("extension_web_browser",
  136.               "open URL in Netscape",
  137.               "You need to have Netscape installed",
  138.               "Misha Dynin <misha@xcf.berkeley.edu>",
  139.               "Misha Dynin, Jamie Zawinski, Spencer Kimball & Peter Mattis",
  140.               "1997",
  141.               N_("<Toolbox>/Xtns/Web Browser/Open URL..."),
  142.               NULL,
  143.               GIMP_EXTENSION,
  144.               nargs, 0,
  145.               args, NULL);
  146. }
  147.  
  148. static void
  149. run (gchar   *name,
  150.      gint     nparams,
  151.      GimpParam  *param,
  152.      gint    *nreturn_vals,
  153.      GimpParam **return_vals)
  154. {
  155.   static GimpParam values[1];
  156.   GimpRunModeType run_mode;
  157.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  158.  
  159.   run_mode = param[0].data.d_int32;
  160.  
  161.   values[0].type = GIMP_PDB_STATUS;
  162.   values[0].data.d_status = status;
  163.  
  164.   *nreturn_vals = 1;
  165.   *return_vals  = values;
  166.  
  167.   if (strcmp (name, "extension_web_browser") == 0)
  168.     {
  169.       switch (run_mode)
  170.     {
  171.     case GIMP_RUN_INTERACTIVE:
  172.       INIT_I18N_UI ();
  173.       /* Possibly retrieve data */
  174.       gimp_get_data ("extension_web_browser", &url_info);
  175.  
  176.       if (!open_url_dialog ())
  177.         return;
  178.       break;
  179.  
  180.     case GIMP_RUN_NONINTERACTIVE:
  181.       /*  Make sure all the arguments are there!  */
  182.       if (nparams != 3)
  183.         {
  184.           status = GIMP_PDB_CALLING_ERROR;
  185.         }
  186.       else
  187.         {
  188.           strncpy (url_info.url, param[1].data.d_string, URL_BUF_SIZE);
  189.           url_info.new_window = param[2].data.d_int32;
  190.         }
  191.       break;
  192.  
  193.     case GIMP_RUN_WITH_LAST_VALS:
  194.       gimp_get_data ("extension_web_browser", &url_info);
  195.       break;
  196.  
  197.     default:
  198.       break;
  199.     }
  200.  
  201.       if (status == GIMP_PDB_SUCCESS)
  202.     {
  203.       if (!open_url (url_info.url, url_info.new_window))
  204.         values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  205.  
  206.       if (run_mode == GIMP_RUN_INTERACTIVE)
  207.         gimp_set_data ("extension_web_browser", &url_info, sizeof (u_info));
  208.  
  209.       values[0].data.d_status = GIMP_PDB_SUCCESS;
  210.     }
  211.       else
  212.     values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  213.     }
  214.   else
  215.     g_assert_not_reached ();
  216. }
  217.  
  218. static gint
  219. start_browser (gchar *prog,
  220.            gchar *url)
  221. {
  222. #ifdef G_OS_WIN32
  223.  
  224.   return (gint) ShellExecute (HWND_DESKTOP, "open", url, NULL, "C:\\", SW_SHOWNORMAL) > 32;
  225.  
  226. #else
  227. #ifndef __EMX__
  228.   pid_t cpid;
  229.  
  230.   if ((cpid = fork()) == 0)
  231.     {
  232.       execlp (prog, prog, url, NULL);
  233.       exit (1);
  234.     }
  235.  
  236.   return (cpid > 0);
  237. #else
  238.   return (spawnlp (P_NOWAIT, prog, prog, url, NULL) != -1);
  239. #endif
  240. #endif /* !G_OS_WIN32 */
  241. }
  242.  
  243. static gint
  244. open_url (gchar *url,
  245.       gint   new_window)
  246. {
  247.   gchar buf[512];
  248.  
  249.   while (isspace (*url))
  250.     ++url;
  251.  
  252. #ifndef G_OS_WIN32
  253.   sprintf (buf, "openURL(%s%s)", url, new_window ? ",new-window" : "");
  254.  
  255.   if (mozilla_remote (buf))
  256.     return (TRUE);
  257. #endif
  258.   return (start_browser (BROWSER_PROGNAME, url));
  259. }
  260.  
  261. static gint
  262. open_url_dialog (void)
  263. {
  264.   GtkWidget *dlg;
  265.   GtkWidget *hbox;
  266.   GtkWidget *button;
  267.   GtkWidget *entry;
  268.   GtkWidget *table;
  269.   GSList    *group;
  270.   gchar      buffer[256];
  271.  
  272.   gimp_ui_init ("webbrowser", FALSE);
  273.  
  274.   dlg = gimp_dialog_new (_("Open URL"), "webbbrowser",
  275.              gimp_standard_help_func, "filters/webbrowser.html",
  276.              GTK_WIN_POS_MOUSE,
  277.              FALSE, TRUE, FALSE,
  278.  
  279.              _("About"), about_callback,
  280.              NULL, NULL, NULL, FALSE, FALSE,
  281.              _("OK"), ok_callback,
  282.              NULL, NULL, NULL, TRUE, FALSE,
  283.              _("Cancel"), gtk_widget_destroy,
  284.              NULL, 1, NULL, FALSE, TRUE,
  285.  
  286.              NULL);
  287.  
  288.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  289.               GTK_SIGNAL_FUNC (gtk_main_quit),
  290.               NULL);
  291.  
  292.   /* table */
  293.   table = gtk_table_new (2, 2, FALSE);
  294.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  295.   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  296.   gtk_container_set_border_width (GTK_CONTAINER (table), 6);
  297.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), table, FALSE, FALSE, 0);
  298.   gtk_widget_show (table);
  299.  
  300.   /* URL */
  301.   entry = gtk_entry_new ();
  302.   gtk_widget_set_usize (entry, 200, 0);
  303.   g_snprintf (buffer, sizeof (buffer), "%s", url_info.url);
  304.   gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  305.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  306.                  _("URL:"), 1.0, 0.5,
  307.                  entry, 1, FALSE);
  308.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  309.               GTK_SIGNAL_FUNC (url_callback),
  310.               &url_info.url);
  311.   gtk_widget_show (entry);
  312.  
  313.   /* Window */
  314.   hbox = gtk_hbox_new (FALSE, 4);
  315.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  316.                  _("Window:"), 1.0, 0.5,
  317.                  hbox, 1, FALSE);
  318.  
  319.   button = gtk_radio_button_new_with_label (NULL, _("New"));
  320.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
  321.   if (url_info.new_window == OPEN_URL_NEW_WINDOW)
  322.     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
  323.   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  324.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  325.               GTK_SIGNAL_FUNC (new_window_callback),
  326.               (gpointer) OPEN_URL_NEW_WINDOW);
  327.   gtk_widget_show (button);
  328.  
  329.   button = gtk_radio_button_new_with_label (group, _("Current"));
  330.   group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
  331.   if (url_info.new_window == OPEN_URL_CURRENT_WINDOW)
  332.     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
  333.   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  334.   gtk_signal_connect (GTK_OBJECT (button), "toggled",
  335.               GTK_SIGNAL_FUNC (new_window_callback),
  336.               (gpointer) OPEN_URL_CURRENT_WINDOW);
  337.   gtk_widget_show (button);
  338.  
  339.   gtk_widget_show (dlg);
  340.  
  341.   gtk_main ();
  342.   gdk_flush ();
  343.  
  344.   return run_flag;
  345. }
  346.  
  347. static void
  348. ok_callback (GtkWidget *widget,
  349.          gpointer   data)
  350. {
  351.   run_flag = TRUE;
  352.  
  353.   gtk_widget_destroy (GTK_WIDGET (data));
  354. }
  355.  
  356. static void
  357. about_callback (GtkWidget *widget,
  358.         gpointer   data)
  359. {
  360.   open_url ("http://www.xcf.berkeley.edu/~misha/gimp/", OPEN_URL_NEW_WINDOW);
  361. }
  362.  
  363. static void
  364. new_window_callback (GtkWidget *widget,
  365.              gpointer   data)
  366. {
  367.   if (GTK_TOGGLE_BUTTON (widget)->active)
  368.     {
  369.       url_info.new_window = GPOINTER_TO_INT (data);
  370.     }
  371. }
  372.  
  373. static void
  374. url_callback (GtkWidget *widget,
  375.           gpointer   data)
  376. {
  377.   strncpy (url_info.url, gtk_entry_get_text (GTK_ENTRY (widget)), URL_BUF_SIZE);
  378. }
  379.  
  380. #ifndef G_OS_WIN32
  381.  
  382. /* -*- Mode:C; tab-width: 8 -*-
  383.  * remote.c --- remote control of Netscape Navigator for Unix.
  384.  * version 1.1.3, for Netscape Navigator 1.1 and newer.
  385.  *
  386.  * Copyright © 1996 Netscape Communications Corporation, all rights reserved.
  387.  * Created: Jamie Zawinski <jwz@netscape.com>, 24-Dec-94.
  388.  *
  389.  * To compile:
  390.  *
  391.  *    cc -o netscape-remote remote.c -DSTANDALONE -lXmu -lX11
  392.  *
  393.  * To use:
  394.  *
  395.  *    netscape-remote -help
  396.  *
  397.  * Documentation for the protocol which this code implements may be found at:
  398.  *
  399.  *    http://home.netscape.com/newsref/std/x-remote.html
  400.  *
  401.  * Bugs and commentary to x_cbug@netscape.com.
  402.  */
  403.  
  404.  
  405.  
  406. /* vroot.h is a header file which lets a client get along with `virtual root'
  407.    window managers like swm, tvtwm, olvwm, etc.  If you don't have this header
  408.    file, you can find it at "http://home.netscape.com/newsref/std/vroot.h".
  409.    If you don't care about supporting virtual root window managers, you can
  410.    comment this line out.
  411.  */
  412. /* #include "vroot.h" */
  413. /* Begin vroot.h */
  414. /*****************************************************************************/
  415. /**                   Copyright 1991 by Andreas Stolcke                     **/
  416. /**               Copyright 1990 by Solbourne Computer Inc.                 **/
  417. /**                          Longmont, Colorado                             **/
  418. /**                                                                         **/
  419. /**                           All Rights Reserved                           **/
  420. /**                                                                         **/
  421. /**    Permission to use, copy, modify, and distribute this software and    **/
  422. /**    its documentation  for  any  purpose  and  without  fee is hereby    **/
  423. /**    granted, provided that the above copyright notice appear  in  all    **/
  424. /**    copies and that  the  name of Solbourne not be used in advertising   **/
  425. /**    in publicity pertaining to distribution of the  software  without    **/
  426. /**    specific, written prior permission.                                  **/
  427. /**                                                                         **/
  428. /**    ANDREAS STOLCKE AND SOLBOURNE COMPUTER INC. DISCLAIMS ALL WARRANTIES **/
  429. /**    WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF    **/
  430. /**    MERCHANTABILITY  AND  FITNESS,  IN  NO  EVENT SHALL ANDREAS STOLCKE  **/
  431. /**    OR SOLBOURNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL    **/
  432. /**    DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA   **/
  433. /**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
  434. /**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
  435. /**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
  436. /*****************************************************************************/
  437. /*
  438.  * vroot.h -- Virtual Root Window handling header file
  439.  *
  440.  * This header file redefines the X11 macros RootWindow and DefaultRootWindow,
  441.  * making them look for a virtual root window as provided by certain `virtual'
  442.  * window managers like swm and tvtwm. If none is found, the ordinary root
  443.  * window is returned, thus retaining backward compatibility with standard
  444.  * window managers.
  445.  * The function implementing the virtual root lookup remembers the result of
  446.  * its last invocation to avoid overhead in the case of repeated calls
  447.  * on the same display and screen arguments. 
  448.  * The lookup code itself is taken from Tom LaStrange's ssetroot program.
  449.  *
  450.  * Most simple root window changing X programs can be converted to using
  451.  * virtual roots by just including
  452.  *
  453.  * #include <X11/vroot.h>
  454.  *
  455.  * after all the X11 header files.  It has been tested on such popular
  456.  * X clients as xphoon, xfroot, xloadimage, and xaqua.
  457.  * It also works with the core clients xprop, xwininfo, xwd, and editres
  458.  * (and is necessary to get those clients working under tvtwm).
  459.  * It does NOT work with xsetroot; get the xsetroot replacement included in
  460.  * the tvtwm distribution instead.
  461.  *
  462.  * Andreas Stolcke <stolcke@ICSI.Berkeley.EDU>, 9/7/90
  463.  * - replaced all NULL's with properly cast 0's, 5/6/91
  464.  * - free children list (suggested by Mark Martin <mmm@cetia.fr>), 5/16/91
  465.  * - include X11/Xlib.h and support RootWindowOfScreen, too 9/17/91
  466.  */
  467.  
  468. #ifndef _VROOT_H_
  469. #define _VROOT_H_
  470.  
  471.  
  472. static Window
  473. VirtualRootWindowOfScreen(screen)
  474.     Screen *screen;
  475. {
  476.     static Screen *save_screen = (Screen *)0;
  477.     static Window root = (Window)0;
  478.  
  479.     if (screen != save_screen) {
  480.         Display *dpy = DisplayOfScreen(screen);
  481.         Atom __SWM_VROOT = None;
  482.         int i;
  483.         Window rootReturn, parentReturn, *children;
  484.         unsigned int numChildren;
  485.  
  486.         root = RootWindowOfScreen(screen);
  487.  
  488.         /* go look for a virtual root */
  489.         __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
  490.         if (XQueryTree(dpy, root, &rootReturn, &parentReturn,
  491.                  &children, &numChildren)) {
  492.             for (i = 0; i < numChildren; i++) {
  493.                 Atom actual_type;
  494.                 int actual_format;
  495.                 unsigned long nitems, bytesafter;
  496.                 Window *newRoot = (Window *)0;
  497.  
  498.                 if (XGetWindowProperty(dpy, children[i],
  499.                     __SWM_VROOT, 0, 1, False, XA_WINDOW,
  500.                     &actual_type, &actual_format,
  501.                     &nitems, &bytesafter,
  502.                     (unsigned char **) &newRoot) == Success
  503.                     && newRoot) {
  504.                     root = *newRoot;
  505.                     break;
  506.                 }
  507.             }
  508.             if (children)
  509.                 XFree((char *)children);
  510.         }
  511.  
  512.         save_screen = screen;
  513.     }
  514.  
  515.     return root;
  516. }
  517.  
  518. #undef RootWindowOfScreen
  519. #define RootWindowOfScreen(s) VirtualRootWindowOfScreen(s)
  520.  
  521. #undef RootWindow
  522. #define RootWindow(dpy,screen) VirtualRootWindowOfScreen(ScreenOfDisplay(dpy,screen))
  523.  
  524. #undef DefaultRootWindow
  525. #define DefaultRootWindow(dpy) VirtualRootWindowOfScreen(DefaultScreenOfDisplay(dpy))
  526.  
  527. #endif /* _VROOT_H_ */
  528. /* End vroot.h */
  529.  
  530.  
  531. #ifdef DIAGNOSTIC
  532. #ifdef STANDALONE
  533.  static const char *progname = 0;
  534.  static const char *expected_mozilla_version = "1.1";
  535. #else  /* !STANDALONE */
  536.  extern const char *progname;
  537.  extern const char *expected_mozilla_version;
  538. #endif /* !STANDALONE */
  539. #endif /* DIAGNOSTIC */
  540.  
  541. #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
  542. #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
  543. #define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
  544. #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
  545. static Atom XA_MOZILLA_VERSION  = 0;
  546. static Atom XA_MOZILLA_LOCK     = 0;
  547. static Atom XA_MOZILLA_COMMAND  = 0;
  548. static Atom XA_MOZILLA_RESPONSE = 0;
  549.  
  550. static void
  551. mozilla_remote_init_atoms (Display *dpy)
  552. {
  553.   if (! XA_MOZILLA_VERSION)
  554.     XA_MOZILLA_VERSION = XInternAtom (dpy, MOZILLA_VERSION_PROP, False);
  555.   if (! XA_MOZILLA_LOCK)
  556.     XA_MOZILLA_LOCK = XInternAtom (dpy, MOZILLA_LOCK_PROP, False);
  557.   if (! XA_MOZILLA_COMMAND)
  558.     XA_MOZILLA_COMMAND = XInternAtom (dpy, MOZILLA_COMMAND_PROP, False);
  559.   if (! XA_MOZILLA_RESPONSE)
  560.     XA_MOZILLA_RESPONSE = XInternAtom (dpy, MOZILLA_RESPONSE_PROP, False);
  561. }
  562.  
  563. static Window
  564. mozilla_remote_find_window (Display *dpy)
  565. {
  566.   int i;
  567.   Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
  568.   Window root2, parent, *kids;
  569.   unsigned int nkids;
  570.   Window result = 0;
  571.  
  572.   if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
  573.     {
  574. #ifdef DIAGNOSTIC
  575.       fprintf (stderr, "%s: XQueryTree failed on display %s\n", progname,
  576.            DisplayString (dpy));
  577. #endif
  578.       return (0);
  579.     }
  580.  
  581.   /* root != root2 is possible with virtual root WMs. */
  582.  
  583.   if (! (kids && nkids))
  584.     {
  585. #ifdef DIAGNOSTIC
  586.       fprintf (stderr, "%s: root window has no children on display %s\n",
  587.            progname, DisplayString (dpy));
  588. #endif
  589.       return (0);
  590.     }
  591.  
  592.   for (i = nkids-1; i >= 0; i--)
  593.     {
  594.       Atom type;
  595.       int format;
  596.       unsigned long nitems, bytesafter;
  597.       unsigned char *version = 0;
  598.       Window w = XmuClientWindow (dpy, kids[i]);
  599.       int status = XGetWindowProperty (dpy, w, XA_MOZILLA_VERSION,
  600.                        0, (65536 / sizeof (long)),
  601.                        False, XA_STRING,
  602.                        &type, &format, &nitems, &bytesafter,
  603.                        &version);
  604.       if (! version)
  605.     continue;
  606.  
  607.       XFree (version);
  608.       if (status == Success && type != None)
  609.     {
  610.       result = w;
  611.       break;
  612.     }
  613.     }
  614.  
  615.   return result;
  616. }
  617.  
  618. static char *lock_data = 0;
  619.  
  620. static int
  621. mozilla_remote_obtain_lock (Display *dpy, Window window)
  622. {
  623.   Bool locked = False;
  624.   Bool waited = False;
  625.  
  626.   if (! lock_data)
  627.     {
  628.       lock_data = (char *) malloc (255);
  629.       sprintf (lock_data, "pid%d@", getpid ());
  630.       if (gethostname (lock_data + strlen (lock_data), 100))
  631.     {
  632.       return (-1);
  633.     }
  634.     }
  635.  
  636.   do
  637.     {
  638.       int result;
  639.       Atom actual_type;
  640.       int actual_format;
  641.       unsigned long nitems, bytes_after;
  642.       unsigned char *data = 0;
  643.  
  644.       XGrabServer (dpy);   /* ################################# DANGER! */
  645.  
  646.       result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK,
  647.                    0, (65536 / sizeof (long)),
  648.                    False, /* don't delete */
  649.                    XA_STRING,
  650.                    &actual_type, &actual_format,
  651.                    &nitems, &bytes_after,
  652.                    &data);
  653.       if (result != Success || actual_type == None)
  654.     {
  655.       /* It's not now locked - lock it. */
  656. #ifdef DEBUG_PROPS
  657.       fprintf (stderr, "%s: (writing " MOZILLA_LOCK_PROP
  658.            " \"%s\" to 0x%x)\n",
  659.            progname, lock_data, (unsigned int) window);
  660. #endif
  661.       XChangeProperty (dpy, window, XA_MOZILLA_LOCK, XA_STRING, 8,
  662.                PropModeReplace, (unsigned char *) lock_data,
  663.                strlen (lock_data));
  664.       locked = True;
  665.     }
  666.  
  667.       XUngrabServer (dpy); /* ################################# danger over */
  668.       XSync (dpy, False);
  669.  
  670.       if (! locked)
  671.     {
  672.       /* We tried to grab the lock this time, and failed because someone
  673.          else is holding it already.  So, wait for a PropertyDelete event
  674.          to come in, and try again. */
  675.  
  676. #ifdef DIAGNOSTIC
  677.       fprintf (stderr, "%s: window 0x%x is locked by %s; waiting...\n",
  678.            progname, (unsigned int) window, data);
  679. #endif
  680.       waited = True;
  681.  
  682.       while (1)
  683.         {
  684.           XEvent event;
  685.           XNextEvent (dpy, &event);
  686.           if (event.xany.type == DestroyNotify &&
  687.           event.xdestroywindow.window == window)
  688.         {
  689. #ifdef DIAGNOSTIC
  690.           fprintf (stderr, "%s: window 0x%x unexpectedly destroyed.\n",
  691.                progname, (unsigned int) window);
  692. #endif
  693.           return (6);
  694.         }
  695.           else if (event.xany.type == PropertyNotify &&
  696.                event.xproperty.state == PropertyDelete &&
  697.                event.xproperty.window == window &&
  698.                event.xproperty.atom == XA_MOZILLA_LOCK)
  699.         {
  700.           /* Ok!  Someone deleted their lock, so now we can try
  701.              again. */
  702. #ifdef DEBUG_PROPS
  703.           fprintf (stderr, "%s: (0x%x unlocked, trying again...)\n",
  704.                progname, (unsigned int) window);
  705. #endif
  706.           break;
  707.         }
  708.         }
  709.     }
  710.       if (data)
  711.     XFree (data);
  712.     }
  713.   while (! locked);
  714.  
  715. #ifdef DIAGNOSTIC
  716.   if (waited)
  717.     fprintf (stderr, "%s: obtained lock.\n", progname);
  718. #endif
  719.   return (0);
  720. }
  721.  
  722.  
  723. static void
  724. mozilla_remote_free_lock (Display *dpy, Window window)
  725. {
  726.   int result;
  727.   Atom actual_type;
  728.   int actual_format;
  729.   unsigned long nitems, bytes_after;
  730.   unsigned char *data = 0;
  731.  
  732. #ifdef DEBUG_PROPS
  733.       fprintf (stderr, "%s: (deleting " MOZILLA_LOCK_PROP
  734.            " \"%s\" from 0x%x)\n",
  735.            progname, lock_data, (unsigned int) window);
  736. #endif
  737.  
  738.   result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK,
  739.                    0, (65536 / sizeof (long)),
  740.                    True, /* atomic delete after */
  741.                    XA_STRING,
  742.                    &actual_type, &actual_format,
  743.                    &nitems, &bytes_after,
  744.                    &data);
  745.   if (result != Success)
  746.     {
  747. #ifdef DIAGNOSTIC
  748.       fprintf (stderr, "%s: unable to read and delete " MOZILLA_LOCK_PROP
  749.            " property\n",
  750.            progname);
  751. #endif
  752.       return;
  753.     }
  754.   else if (!data || !*data)
  755.     {
  756. #ifdef DIAGNOSTIC
  757.       fprintf (stderr, "%s: invalid data on " MOZILLA_LOCK_PROP
  758.            " of window 0x%x.\n",
  759.            progname, (unsigned int) window);
  760. #endif
  761.       return;
  762.     }
  763.   else if (strcmp ((char *) data, lock_data))
  764.     {
  765. #ifdef DIAGNOSTIC
  766.       fprintf (stderr, "%s: " MOZILLA_LOCK_PROP
  767.            " was stolen!  Expected \"%s\", saw \"%s\"!\n",
  768.            progname, lock_data, data);
  769. #endif
  770.       return;
  771.     }
  772.  
  773.   if (data)
  774.     XFree (data);
  775. }
  776.  
  777.  
  778. static int
  779. mozilla_remote_command (Display *dpy, Window window, const char *command)
  780. {
  781.   int result = 0;
  782.   Bool done = False;
  783.  
  784. #ifdef DEBUG_PROPS
  785.   fprintf (stderr, "%s: (writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n",
  786.        progname, command, (unsigned int) window);
  787. #endif
  788.  
  789.   XChangeProperty (dpy, window, XA_MOZILLA_COMMAND, XA_STRING, 8,
  790.            PropModeReplace, (unsigned char *) command,
  791.            strlen (command));
  792.  
  793.   while (!done)
  794.     {
  795.       XEvent event;
  796.       XNextEvent (dpy, &event);
  797.       if (event.xany.type == DestroyNotify &&
  798.       event.xdestroywindow.window == window)
  799.     {
  800.       /* Print to warn user...*/
  801. #ifdef DIAGNOSTIC
  802.       fprintf (stderr, "%s: window 0x%x was destroyed.\n",
  803.            progname, (unsigned int) window);
  804. #endif
  805.       result = 6;
  806.       goto DONE;
  807.     }
  808.       else if (event.xany.type == PropertyNotify &&
  809.            event.xproperty.state == PropertyNewValue &&
  810.            event.xproperty.window == window &&
  811.            event.xproperty.atom == XA_MOZILLA_RESPONSE)
  812.     {
  813.       Atom actual_type;
  814.       int actual_format;
  815.       unsigned long nitems, bytes_after;
  816.       unsigned char *data = 0;
  817.  
  818.       result = XGetWindowProperty (dpy, window, XA_MOZILLA_RESPONSE,
  819.                        0, (65536 / sizeof (long)),
  820.                        True, /* atomic delete after */
  821.                        XA_STRING,
  822.                        &actual_type, &actual_format,
  823.                        &nitems, &bytes_after,
  824.                        &data);
  825. #ifdef DEBUG_PROPS
  826.       if (result == Success && data && *data)
  827.         {
  828.           fprintf (stderr, "%s: (server sent " MOZILLA_RESPONSE_PROP
  829.                " \"%s\" to 0x%x.)\n",
  830.                progname, data, (unsigned int) window);
  831.         }
  832. #endif
  833.  
  834.       if (result != Success)
  835.         {
  836. #ifdef DIAGNOSTIC
  837.           fprintf (stderr, "%s: failed reading " MOZILLA_RESPONSE_PROP
  838.                " from window 0x%0x.\n",
  839.                progname, (unsigned int) window);
  840. #endif
  841.           result = 6;
  842.           done = True;
  843.         }
  844.       else if (!data || strlen((char *) data) < 5)
  845.         {
  846. #ifdef DIAGNOSTIC
  847.           fprintf (stderr, "%s: invalid data on " MOZILLA_RESPONSE_PROP
  848.                " property of window 0x%0x.\n",
  849.                progname, (unsigned int) window);
  850. #endif
  851.           result = 6;
  852.           done = True;
  853.         }
  854.       else if (*data == '1')    /* positive preliminary reply */
  855.         {
  856. #ifdef DIAGNOSTIC
  857.           fprintf (stderr, "%s: %s\n", progname, data + 4);
  858. #endif
  859.           /* keep going */
  860.           done = False;
  861.         }
  862. #if 1
  863.       else if (!strncmp ((char *)data, "200", 3)) /* positive completion */
  864.         {
  865.           result = 0;
  866.           done = True;
  867.         }
  868. #endif
  869.       else if (*data == '2')        /* positive completion */
  870.         {
  871. #ifdef DIAGNOSTIC
  872.           fprintf (stderr, "%s: %s\n", progname, data + 4);
  873. #endif
  874.           result = 0;
  875.           done = True;
  876.         }
  877.       else if (*data == '3')    /* positive intermediate reply */
  878.         {
  879. #ifdef DIAGNOSTIC
  880.           fprintf (stderr, "%s: internal error: "
  881.                "server wants more information?  (%s)\n",
  882.                progname, data);
  883. #endif
  884.           result = 3;
  885.           done = True;
  886.         }
  887.       else if (*data == '4' ||    /* transient negative completion */
  888.            *data == '5')    /* permanent negative completion */
  889.         {
  890. #ifdef DIAGNOSTIC
  891.           fprintf (stderr, "%s: %s\n", progname, data + 4);
  892. #endif
  893.           result = (*data - '0');
  894.           done = True;
  895.         }
  896.       else
  897.         {
  898. #ifdef DIAGNOSTIC
  899.           fprintf (stderr,
  900.                "%s: unrecognised " MOZILLA_RESPONSE_PROP
  901.                " from window 0x%x: %s\n",
  902.                progname, (unsigned int) window, data);
  903. #endif
  904.           result = 6;
  905.           done = True;
  906.         }
  907.  
  908.       if (data)
  909.         XFree (data);
  910.     }
  911. #ifdef DEBUG_PROPS
  912.       else if (event.xany.type == PropertyNotify &&
  913.            event.xproperty.window == window &&
  914.            event.xproperty.state == PropertyDelete &&
  915.            event.xproperty.atom == XA_MOZILLA_COMMAND)
  916.     {
  917.       fprintf (stderr, "%s: (server 0x%x has accepted "
  918.            MOZILLA_COMMAND_PROP ".)\n",
  919.            progname, (unsigned int) window);
  920.     }
  921. #endif /* DEBUG_PROPS */
  922.     }
  923.  
  924.  DONE:
  925.  
  926.   return result;
  927. }
  928.  
  929. static int
  930. mozilla_remote (char *command)
  931. {
  932.   int status = 0;
  933.   Display *dpy;
  934.   Window window;
  935.  
  936.   if (!(dpy = XOpenDisplay (NULL)))
  937.     return (0);
  938.  
  939.   mozilla_remote_init_atoms (dpy);
  940.  
  941.   if (!(window = mozilla_remote_find_window (dpy)))
  942.     return (0);
  943.  
  944.   XSelectInput (dpy, window, (PropertyChangeMask|StructureNotifyMask));
  945.  
  946.   if (mozilla_remote_obtain_lock (dpy, window))
  947.     return (0);
  948.  
  949.   status = mozilla_remote_command (dpy, window, command);
  950.  
  951.   /* When status = 6, it means the window has been destroyed */
  952.   /* It is invalid to free the lock when window is destroyed. */
  953.  
  954.   if ( status != 6 )
  955.     mozilla_remote_free_lock (dpy, window);
  956.  
  957.   return (!status);
  958. }
  959.  
  960. #endif /* !G_OS_WIN32 */
  961.